#include <iostream>
#include <sstream>
#include <ruby.h>
#include "m3g/m3g.hpp"
#include "ruby-m3g.hpp"
using namespace m3g;
using namespace std;

void ruby_AnimationTrack_mark (AnimationTrack* ptr)
{
    vector<Object3D*> objs;
    extract_all_references (ptr, objs);
    for (int i = 0; i < (int)objs.size(); i++) {
        VALUE val_obj = (VALUE)objs[i]->getExportedEntity();
        if (val_obj)
            rb_gc_mark (val_obj);
    }
}

void ruby_AnimationTrack_free (AnimationTrack* ptr)
{
    if (valid_ptr((unsigned char*)ptr, sizeof(*ptr))) {
        __TRY__;
        ptr->~AnimationTrack ();
        __CATCH__;
    }
    ruby_xfree (ptr);
}

VALUE ruby_AnimationTrack_allocate (VALUE self)
{
    void* p = ruby_xmalloc (sizeof(AnimationTrack));
    return Data_Wrap_Struct (self, ruby_AnimationTrack_mark, ruby_AnimationTrack_free, p);
}

VALUE ruby_AnimationTrack_initialize (VALUE self, VALUE val_keyframe_sequence, VALUE val_property)
{
    AnimationTrack* p;
    Data_Get_Struct (self, AnimationTrack, p);
    KeyframeSequence* key_seq;
    Data_Get_Struct (val_keyframe_sequence, KeyframeSequence, key_seq);
    int property = NUM2INT (val_property);
    __TRY__;
    new (p) AnimationTrack (key_seq, property);
    __CATCH__;
    p->setExportedEntity ((void*)self);
    return self;
}

VALUE ruby_AnimationTrack_get_controller (VALUE self)
{
    AnimationTrack* p;
    Data_Get_Struct (self, AnimationTrack, p);
    AnimationController* anim_controller;
    __TRY__;
    anim_controller = p->getController ();
    __CATCH__;
    return anim_controller ? (VALUE)anim_controller->getExportedEntity() : Qnil;
}

VALUE ruby_AnimationTrack_get_keyframe_sequence (VALUE self)
{
    AnimationTrack* p;
    Data_Get_Struct (self, AnimationTrack, p);
    KeyframeSequence* key_seq;
    __TRY__;
    key_seq = p->getKeyframeSequence ();
    __CATCH__;
    return key_seq ? (VALUE)key_seq->getExportedEntity() : Qnil;
}

VALUE ruby_AnimationTrack_get_target_property (VALUE self)
{
    AnimationTrack* p;
    Data_Get_Struct (self, AnimationTrack, p);
    int property;
    __TRY__;
    property = p->getTargetProperty ();
    __CATCH__;
    return INT2NUM(property);
}

VALUE ruby_AnimationTrack_set_controller (VALUE self, VALUE val_controller)
{
    if (!rb_obj_is_kind_of (val_controller, rb_cAnimationController)) {
        rb_raise (rb_eArgError, "wrong argument type (expected M3G::AnimationController)");
    }
    AnimationTrack* p;
    Data_Get_Struct (self, AnimationTrack, p);
    AnimationController* anim_controller;
    Data_Get_Struct (val_controller, AnimationController, anim_controller);
    __TRY__;
    p->setController (anim_controller);
    __CATCH__;
    return Qnil;
}

VALUE ruby_AnimationTrack_print (VALUE self)
{
    AnimationTrack* p;
    Data_Get_Struct (self, AnimationTrack, p);
    ostringstream out;
    __TRY__;
    p->print (out);
    __CATCH__;
    return rb_str_new2 (out.str().c_str());
}


/**
 *
 */
void register_AnimationTrack ()
{
    // AnimationTrack
    rb_cAnimationTrack      = rb_define_class_under (rb_mM3G, "AnimationTrack",      rb_cObject3D);

    rb_define_const (rb_cAnimationTrack, "ALPHA",          INT2NUM(AnimationTrack::ALPHA));
    rb_define_const (rb_cAnimationTrack, "AMBIENT_COLOR",  INT2NUM(AnimationTrack::AMBIENT_COLOR));
    rb_define_const (rb_cAnimationTrack, "COLOR",          INT2NUM(AnimationTrack::COLOR));
    rb_define_const (rb_cAnimationTrack, "CROP",           INT2NUM(AnimationTrack::CROP));
    rb_define_const (rb_cAnimationTrack, "DENSITY",        INT2NUM(AnimationTrack::DENSITY));
    rb_define_const (rb_cAnimationTrack, "DIFFUSE_COLOR",  INT2NUM(AnimationTrack::DIFFUSE_COLOR));
    rb_define_const (rb_cAnimationTrack, "EMISSIVE_COLOR", INT2NUM(AnimationTrack::EMISSIVE_COLOR));
    rb_define_const (rb_cAnimationTrack, "FAR_DISTANCE",   INT2NUM(AnimationTrack::FAR_DISTANCE));
    rb_define_const (rb_cAnimationTrack, "FIELD_OF_VIEW",  INT2NUM(AnimationTrack::FIELD_OF_VIEW));
    rb_define_const (rb_cAnimationTrack, "INTENSITY",      INT2NUM(AnimationTrack::INTENSITY));
    rb_define_const (rb_cAnimationTrack, "MORPH_WEIGHTS",  INT2NUM(AnimationTrack::MORPH_WEIGHTS));
    rb_define_const (rb_cAnimationTrack, "NEAR_DISTANCE",  INT2NUM(AnimationTrack::NEAR_DISTANCE));
    rb_define_const (rb_cAnimationTrack, "ORIENTATION",    INT2NUM(AnimationTrack::ORIENTATION));
    rb_define_const (rb_cAnimationTrack, "PICKABILITY",    INT2NUM(AnimationTrack::PICKABILITY));
    rb_define_const (rb_cAnimationTrack, "SCALE",          INT2NUM(AnimationTrack::SCALE));
    rb_define_const (rb_cAnimationTrack, "SHININESS",      INT2NUM(AnimationTrack::SHININESS));
    rb_define_const (rb_cAnimationTrack, "SPECULAR_COLOR", INT2NUM(AnimationTrack::SPECULAR_COLOR));
    rb_define_const (rb_cAnimationTrack, "SPOT_ANGLE",     INT2NUM(AnimationTrack::SPOT_ANGLE));
    rb_define_const (rb_cAnimationTrack, "SPOT_EXPONENT",  INT2NUM(AnimationTrack::SPOT_EXPONENT));
    rb_define_const (rb_cAnimationTrack, "TRANSLATION",    INT2NUM(AnimationTrack::TRANSLATION));
    rb_define_const (rb_cAnimationTrack, "VISIBILITY",     INT2NUM(AnimationTrack::VISIBILITY));

    rb_define_alloc_func     (rb_cAnimationTrack, ruby_AnimationTrack_allocate);
    rb_define_private_method (rb_cAnimationTrack, "initialize", (VALUE(*)(...))ruby_AnimationTrack_initialize,            2);

    rb_define_method (rb_cAnimationTrack, "controller",         (VALUE(*)(...))ruby_AnimationTrack_get_controller,        0);
    rb_define_method (rb_cAnimationTrack, "keyframe_sequence",  (VALUE(*)(...))ruby_AnimationTrack_get_keyframe_sequence, 0);
    rb_define_method (rb_cAnimationTrack, "target_property",    (VALUE(*)(...))ruby_AnimationTrack_get_target_property,   0);
    rb_define_method (rb_cAnimationTrack, "controller=",        (VALUE(*)(...))ruby_AnimationTrack_set_controller,        1);
    rb_define_method (rb_cAnimationTrack, "to_s",               (VALUE(*)(...))ruby_AnimationTrack_print,                 0);
}
